home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of MacTutor - S…e Code for Volumes 1 to 5
/
The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin
/
Source Code
/
#46 (Jul 89)
/
Terminal
/
term.asm
< prev
next >
Wrap
Assembly Source File
|
1989-04-17
|
20KB
|
814 lines
; File: Term.asm
;----------------------------------------------------------------------
; Term.asm is a simple terminal program, that shows how to use low
; level serial I/O routines.
; written by Frank Henriquez
;
; The code that handles the transfers between the clipboard and Text Edit
; is based on the code in chapter 3 of Dan Weston's
; "The Complete Book of Macintosh Assembly Language Programming Volume II"
; Some coding ideas were borrowed from the program "Rose.Asm"
; by Victor Barger, that appeared in the January 1987 issue of MacTutor.
;
; 7/15/87 Initial Turbo Pascal version, using Quickdraw for text.
; 9/1/87 assembly language translation.
; 10/10/87 modified to use TextEdit.
; 10/18/87 fully functional version.
; 10/20/87 text handling improved, Baud rates added.
; 1/14/88 modified to use external serial I/O routines.
;----------------------------------------------------------------------
; Register usage:
; d0: general purpose, also holds the menu#.
; d1: general purpose, also menu item# and the Modify value.
; d2: general purpose.
; d3, d4, d5, d6, d7 : not used.
; a0: general purpose, used for buffers and parameter blocks.
; a1: general purpose.
; a2: holds TextEdit handle.
; a3: general purpose.
; a4, a6 : not used.
; a5, a7 : system use.
;----------------------------------------------------------------------
; pre-compiled routines from Serial I/O.asm
XDEF OpenSerial
XDEF Config
XDEF GetSerial
XDEF PutSerial
XDEF SetBuf
; Standard include files
Include Traps.D
Include Toolequ.D
Include Sysequ.D
Include Quickequ.D
;----------- Program constants -------------
AppleMenu equ 1 ; Apple
AboutItem equ 1 ; First item in Apple menu
FileMenu equ 2 ; File
QuitItem equ 1 ; First item in File menu
EditMenu equ 3 ; Edit
UndoItem equ 1 ; Items in Edit menu
CutItem equ 3
CopyItem equ 4
PasteItem equ 5
ClearItem equ 6
CmdMenu equ 4 ; Command menu
EraseItem equ 1 ; Erase screen
B300 equ 3 ; 300 baud
B1200 equ 4 ; 1200 baud
B2400 equ 5 ; 2400 baud
B9600 equ 6
AboutDialog equ 1 ; About dialog
ButtonItem equ 1 ; First item in DITL
WindID equ 1
PortA equ -6
PortB equ -8
baud300 equ 380
baud1200 equ 94
baud2400 equ 46
baud9600 equ 10
stop10 equ 16384
noParity equ 8192
data8 equ 3072
Config300 equ baud300 + data8 + stop10 + noParity
Config1200 equ baud1200 + data8 + stop10 + noParity
Config2400 equ baud2400 + data8 + stop10 + noParity
Config9600 equ baud9600 + data8 + stop10 + noParity
;---------- Program starts here ------------
Start
bsr.s InitSerial
bsr InitManagers
bsr SetupMenu
bsr SetupWindow
bsr SetupTextEdit
bsr CliptoTE
EventLoop
_SystemTask ; check for DA calls
move.l a2,-(sp) ; get handle to text record
_TEIdle ; blink cursor etc.
bsr GetSerCh ; get a character from the modem port
bsr PutScreen ; and put it on the screen
clr.w -(sp)
move.w #$0fff,-(sp) ; look for all events
pea EventRecord(a5)
_GetNextEvent ; get the next event
move.w (sp)+,d0
beq.s EventLoop ; loop until an event
bsr HandleEvent ; take care of the event
beq.s EventLoop ; time to quit if not zero
move.l a2,-(sp)
_TEDispose
clr.w -(sp)
move.w #PortA,-(sp) ; modem port
clr.l -(sp) ; reset default serial buffer
jsr SetBuf
addq.l #2,sp
_ExittoShell
;-------------- InitSerial ------------------
; Open serial port for reads and writes, set
; the baud rate to 1200 baud and increase the
; size of the serial input buffer to 512 bytes
InitSerial
clr.w -(sp) ; room for routine results
move.w #PortA,-(sp) ; open the modem port (Port A)
jsr OpenSerial
tst.w (sp) ; if Open failed, beep and quit.
bne.s Abort
move.w #PortA,-(sp)
move.w #Config1200,-(sp) ; set modem port to 1200 baud
jsr Config
tst.w (sp) ; if Config failed, beep and quit.
bne.s Abort
move.w #PortA,-(sp) ; make the serial input buffer
move.l #512,-(sp) ; 512 bytes big. Prevents data loss.
jsr SetBuf
move.w (sp)+,d0 ; clean up the stack, and abort if
bne.s Abort ; SetBuf failed.
rts
;--------------- Abort --------------------
Abort
move.w #30,-(sp)
_Sysbeep
_ExitToShell ; jump back to Finder
;------------ InitManagers -----------------
InitManagers
_MoreMasters ; prevents heap fragmentation
move.l #$ffffffff,d0
_NewHandle ; compact heap
pea -4(a5) ; QuickDraw globals
_InitGraf
_InitFonts
move.l #$0000ffff,d0 ; flush all events
_FlushEvents
_InitWindows
_InitMenus
clr.l -(sp) ; no restart procedure
_InitDialogs
_TEInit
_InitCursor
rts
;------------- SetupMenu ------------------
SetupMenu
;Apple menu:
clr.l -(sp)
move.w #AppleMenu, -(sp)
_GetRMenu ; get Apple menu from the rsrc file
move.l (sp),AppleMHdl(a5) ; save handle to menu
move.l (sp),-(sp)
clr -(sp)
_InsertMenu
move.l #'DRVR',-(sp)
_AddResMenu ; add DA's to Apple menu
;File menu:
clr.l -(sp)
move.w #FileMenu,-(sp)
_GetRMenu
move.l (sp),FileMHdl(a5) ; save handle to menu
clr -(sp)
_InsertMenu
;Edit menu:
clr.l -(sp)
move.w #EditMenu,-(sp)
_GetRMenu
move.l (sp),EditMHdl(a5) ; save handle to menu
clr -(sp)
_InsertMenu
;Command menu:
clr.l -(sp)
move.w #CmdMenu,-(sp)
_GetRMenu
move.l (sp),CmdMHdl(a5) ; save handle to menu
clr -(sp)
_InsertMenu
move.l CmdMHdl(a5),-(sp) ; put handle on stack
move.w #B1200,d0
move.w d0,BaudChk(a5)
move.w d0,-(sp)
move.w #-1,-(sp) ; next to the 1200 baud item
_CheckItem
_DrawMenuBar
rts
;------------ SetupWindow -----------------
SetupWindow
clr.l -(sp)
move.w #WindID,-(sp)
pea WindowStorage(a5)
move.l #-1,-(sp) ; make it the top window
_GetNewWindow ; load it from the rsrc file
move.l (sp),WindowHdl(a5) ; save handle
_SetPort ; make it the current port
move.w #monaco,-(sp) ; Monaco
_TextFont
move.w #9,-(sp) ; in 9 pt.
_TextSize
rts
;---------- SetupTextEdit ----------------
SetupTextEdit
clr.l -(sp) ; space for text handle
pea DestRect ; DestRect Rectangle
pea ViewRect ; ViewRect Rectangle
_TENew
move.l (sp)+,a0 ; save text handle
move.l a0,a2 ; in a2
move.l (a0),a0
move.b #-1,teCROnly(a0)
move.w #-1,-(sp)
move.l a2,-(sp) ; enable TE auto scroll (128K ROM)
_TEAutoView
rts
;---------------- CliptoTE ---------------
; CliptoTE copies text in the clipboard to the TE scrap
CliptoTE
move.l #0,a3 ; check for TEXT in the desk scrap
bsr.s @1
bmi.s @2 ; leave if none
move.l #0,d0 ; lets get the scrap, but first
_NewHandle ; get a handle for it
move.l a0,a3
bsr.s @1
move.l TEScrpHandle,a0 ; replace old handle to TEScrap
_DisposHandle
move.l a3,TEScrpHandle ; with ours
move.l TEScrpHandle,a0
_GetHandleSize ; get length of our scrap
move.w d0,TEScrpLength
@2 rts ; leave CliptoTE
@1 clr.l -(sp) ; space for result
move.l a3,-(sp)
move.l #'TEXT',-(sp) ; just check for TEXT
pea GSOffset(a5)
_GetScrap
move.l (sp)+,d0 ; return result
rts
;--------------- GetSerCh ----------------
GetSerCh
clr.l -(sp) ; room for character count
move.w #PortA,-(sp) ; if modem port has data, save it
pea IOBuf(a5) ; in this buffer.
jsr GetSerial
move.l (sp)+,CharCnt(a5) ; get the # of characters received.
bra NextEvent
;--------------- PutScreen ----------------
; PutScreen will display the characters in
; the buffer, taking care of some control
; characters. It uses TextEdit for display.
PutScreen
move.l CharCnt(a5),d1 ; skip if no characters in buffer
beq NextEvent
subq.l #1,d1 ; adjust character count for loop
PutTE lea IOBuf(a5),a0 ; point to the character buffers
lea Outbuf(a5),a1
TELoop move.b (a0)+,d0 ; get a char from the input buffer
andi.w #$7f,d0 ; clear out 8th bit
cmpi.w #$20,d0 ; if less than space, then it
blt.s CtrlCh ; must be a Ctrl character
inBuf move.b d0,(a1)+ ; printable character
inLoop dbra d1,TELoop
pea Outbuf(a5) ; print the sanitized output buffer
move.l CharCnt(a5),-(sp)
move.l a2,-(sp)
_TEInsert
move.l a2,-(sp)
_TESelView ; scroll if needed
bra NextEvent ; exit PutScreen
;----- handle control characters ----------
; Control characters are serviced here. A
; lookup table would be more efficient, in
; particular if we were to emulate a real
; terminal (like a VT-52 or VT-100).
CtrlCh cmpi.w #$0d,d0 ; let Text Edit take care of CR
beq.s inBuf
cmpi.w #8,d0 ; backspace
beq.s BS
cmpi.w #7,d0 ; bell
beq.s Bell
cmpi.w #9,d0 ; tab
beq.s Tab
subq.l #1,CharCnt(a5) ; ignore anything else
bra.s inLoop
BS subq.l #1,a1 ; move back outbuf pointer by 1
subq.l #2,CharCnt(a5) ; 1 for BS char, 1 for erased char
beq NextEvent ; no chars to print - all done.
bgt.s inLoop ; continue filtering
move.w d0,-(sp) ; use TEKey to force backspace
move.l a2,-(sp) ; if backed out of input buffer
_TEKey
bra NextEvent
Bell move.w #30,-(sp) ; make a beep
_SysBeep
subq.l #1,CharCnt(a5) ; get rid of character
bra.s inLoop
Tab move.w #4,d2 ; 5 spaces for a tab
@1 move.b #$20,(a1)+ ; put them into the output buffer
dbra d2,@1
addq.l #4,CharCnt(a5) ; adjust the character count
bra.s inLoop ; ( # spaces, -1 for the tab char)
;------------- Handle Event -------------
; This routine is the core of the program.
; The event number is used as an index into
; the EventTable. These entries cover all
; the events that could happen while the
; program is in the main loop.
HandleEvent
move.w Modify(a5),d1
move.w What(a5),d0 ; get event number
add.w d0,d0 ; *2 for table index
move.w EventTable(d0),d0 ; point to routine
jmp EventTable(d0) ; and jump to it
EventTable
dc.w NextEvent-EventTable ; 0 (Null)
dc.w MouseDown-EventTable ; 1 mouse down
dc.w NextEvent-EventTable ; 2 mouse up (Not used)
dc.w KeyDown-EventTable ; 3 key down
dc.w NextEvent-EventTable ; 4 key up (Not used)
dc.w KeyDown-EventTable ; 5 auto-key
dc.w Update-EventTable ; 6 update
dc.w NextEvent-EventTable ; 7 disk inserted (Not used)
dc.w Activate-EventTable ; 8 activate
dc.w NextEvent-EventTable ; 9 abort (Not used)
dc.w NextEvent-EventTable ; 10 network (Not used)
dc.w NextEvent-EventTable ; 11 I/O driver (Not used)
;-------------- Mouse down ----------------
MouseDown
clr.w -(sp) ; space for result
move.l Point(a5),-(sp) ; get mouse coordinates
pea WWindow(a5) ; Event Window
_FindWindow
move (sp)+,d0 ; get region number
add d0,d0 ; *2 for index into table
move.w WindowTable(d0),d0 ; point to routine offset
jmp WindowTable(d0) ; jump to routine
WindowTable
dc.w NextEvent-WindowTable ; In Desk (Not used)
dc.w InMenu-WindowTable ; In Menu Bar
dc.w SystemEvent-WindowTable ; In System Window
dc.w Content-WindowTable ; In Content
dc.w Drag-WindowTable ; In Drag
dc.w NextEvent-WindowTable ; In Grow (Not used)
dc.w QuitRoutine-WindowTable ; In Go Away
;---------------- in Menu -----------------
InMenu
clr.l -(sp) ; make room on stack
move.l Point(a5),-(sp) ; mouse event
_MenuSelect
move.w (sp)+,d0 ; save menu
move.w (sp)+,d1 ; and menu item
Choices ; called by command key too
cmp.w #AppleMenu,d0
beq.s InAppleMenu
cmp.w #FileMenu,d0
beq.s InFileMenu
cmp.w #EditMenu,d0
beq.s InEditMenu
cmp.w #CmdMenu,d0
beq InCmdMenu
ChoiceReturn
bsr UnHiliteMenu ; unhighlight the menu bar
bra NextEvent
;-------------- in InAppleMenu -----------------
; In the Apple menu. If it wasn't About, it must have been a
; desk accessory. If so, open the desk accessory.
InAppleMenu
cmp.w #AboutItem,d1 ; is it About?
beq.s About ; if so go do About...
move.l AppleMHdl(a5),-(sp) ; look in Apple Menu
move.w d1,-(sp)
pea DeskName(a5) ; get Item Name
_GetItem
clr -(sp) ; space for opening result
pea DeskName(a5) ; open Desk Acc
_OpenDeskAcc
move.w (sp)+,d0 ; pop result
GoSetOurPort
bsr SetOurPort
bra.s ChoiceReturn
;--------------- About --------------------
; Set up the About dialog box, and wait for
; the proper click or keypress. End by
; closing the dialog box and setting the
; port to us.
About
clr.l -(sp) ; space for dialog pointer
move #AboutDialog,-(sp) ; dialog rsrc #
pea DStorage(a5)
move.l #-1,-(sp) ; dialog goes on top
_GetNewDialog
move.l (sp),-(sp) ; copy handle for Close
_SetPort ; make dialog box the port
move.l a2,-(sp)
_TEDeActivate
WaitOK
clr.l -(sp) ; clear space for handle
pea ItemHit(a5)
_ModalDialog ; wait for a response
move.w ItemHit(a5),d0 ; look to see what was hit
cmp.w #ButtonItem,d0 ; was it OK?
bne.s WaitOK
_CloseDialog ; handle already on stack
bra.s GoSetOurPort
;---------------- in FileMenu -----------------
inFileMenu
cmp.w #QuitItem,d1 ; is it Quit?
bne.s ChoiceReturn ; no, go get next event
bsr UnHiliteMenu ; unhighlight the menu bar
bra RealQuit ; go Quit
;---------------- in EditMenu -----------------
; The Edit Menu support routines transfer the
; TE scrap to the clipboard, and back. See the
; clipboard chapter in Dan Weston's
; "Assembly Language Programming Volume II"
InEditMenu
bsr SystemEdit ; Desk accessory active?
bne.s ChoiceReturn ; yes, SystemEdit handled it
cmp.w #CutItem,d1
beq.s Cut
cmp.w #CopyItem,d1
beq.s Copy
cmp.w #PasteItem,d1
beq.s Paste
cmp.w #ClearItem,d1
beq.s ClearIt
bra ChoiceReturn
Cut
move.l a2,-(sp)
_TECut ; Cut and copy text
bra.s Convert
Copy
move.l a2,-(sp)
_TECopy ; Copy text to scap
bra.s Convert
Paste
bsr CliptoTE ; from the clipboard
move.l a2,-(sp)
_TEPaste ; Paste
bra ChoiceReturn
ClearIt
move.l a2,-(sp)
_TEDelete ; Clear without copying
Convert
bsr.s TEtoClip ; copy to clipboard
bra ChoiceReturn
;-------------- TEtoClip ---------------------
; TEtoClip copies the TE scrap to the Clipboard
TEtoClip
move.l TEScrpHandle,a0
_GetHandleSize ; get length of our scrap
move.l d0,a3 ; save length
clr.l -(sp) ; space for result
_ZeroScrap ; want to write over scrap
move.l (sp)+,d0
move.l TEScrpHandle,a0
_Hlock ; lock pointer to our scrap
clr.l -(sp) ; space for result
move.l a3,-(sp)
move.l #'TEXT',-(sp) ; just check for TEXT
move.l TEScrpHandle,a0
move.l (a0),-(sp) ; save pointer to TE scrap
_PutScrap
move.l (sp)+,d0
move.l TEScrpHandle,a0
_HUnLock ; unlock handle
rts ; leave TEtoClip
;---------------- in CmdMenu ------------------
InCmdMenu
cmp.w #EraseItem,d1 ; baud rate change?
bne.s InBaud
; it wasn't the baud change command, so fall through to erase screen
move.l #0,-(sp) ; set Selection range to cover
move.l #$ffff,-(sp) ; everything in this TE block.
move.l a2,-(sp)
_TESetSelect
bra.s ClearIt ; and clear them out
;---------------- in BaudMenu -----------------
InBaud
cmp.w #B300,d1
beq.s @1
cmp.w #B1200,d1
beq.s @2
cmp.w #B2400,d1
beq.s @3
cmp.w #B9600,d1
beq.s @4
bra NextEvent
@1 move.w #Config300,SerCon(a5)
bra.s @5
@2 move.w #Config1200,SerCon(a5)
bra.s @5
@3 move.w #Config2400,SerCon(a5)
bra.s @5
@4 move.w #Config9600,SerCon(a5)
@5 move.l CmdMHdl(a5),-(sp)
move.w BaudChk(a5),-(sp) ; uncheck previous item
move.w d1,BaudChk(a5) ; save new baud item
move.w #0,-(sp)
_CheckItem
move.l CmdMHdl(a5),-(sp) ; put handle on stack
move.w BaudChk(a5),-(sp) ; and place a check
move.w #-1,-(sp) ; next to the current baud
_CheckItem
clr.w -(sp)
move.w #PortA,-(sp) ; set the new baud rate
move.w SerCon(a5),-(sp)
jsr Config
move.w (sp)+,d0 ; ignore the result
bra ChoiceReturn
;------------- UnhiliteMenu ---------------
UnhiliteMenu
clr.w -(sp) ; all menus
_HiLiteMenu
rts
;--------------- SystemEdit ----------------
SystemEdit
clr -(sp) ; space for result
move.w d1,-(sp) ; get item in Edit menu
subq #1,(sp) ; SystemEdit is off by 1
_SysEdit
move.b (sp)+,d0
rts
;-------------- SystemEvent ---------------
SystemEvent
pea EventRecord(a5)
move.l WWindow(a5),-(sp)
_SystemClick ; let the system do it
bra NextEvent
;-------------- in Content ----------------
Content
clr.l -(sp) ; room for result
_FrontWindow
move.l (sp)+,d0 ; front window pointer
cmp.l WindowHdl(a5),d0 ; same as our pointer?
beq.s @1 ; ignore it
move.l WWindow(a5),-(sp)
_SelectWindow ; do it, fall through to NextEvent
bra NextEvent
@1 pea Point(a5)
_GlobalToLocal
move.l Point(a5),-(sp)
btst #9,d1
sne d0
move.b d0,-(sp)
move.l a2,-(sp)
_TEClick
bra NextEvent
;---------------- in Drag -----------------
Drag
move.l WWindow(a5),-(sp)
move.l Point(a5),-(sp)
pea WBounds
_DragWindow ; drag window and fall through
bra NextEvent
;--------------- Quit Routine -----------------
QuitRoutine
clr.w -(sp) ; room for result
move.l WindowHdl(a5),-(sp) ; get pointer to open window
move.l Point(a5),-(sp) ; mouse point
_TrackGoAway
move.w (sp)+,d0
beq NextEvent ; didn't really want to quit
RealQuit
move.l WindowHdl(a5),-(sp) ; did want to quit
_CloseWindow
move.w #-1,d0 ; d0 is TRUE - ok to quit
rts
;--------------- Key down -----------------
KeyDown
move.w Message+2(a5),d0 ; get key code and character
btst #8,d1 ; command key pressed?
beq.s SaveCh ; yes, take care of it.
andi.w #$001f,d0 ; convert to a control character
SaveCh move.b d0,IOBuf(a5) ; save it in the buffer
move.l #1,CharCnt(a5) ; send the character typed
clr.w -(sp) ; room for dummy result
move.w #PortA,-(sp) ; in the input buffer
pea IOBuf(a5)
pea CharCnt(a5)
jsr PutSerial ; and send it out the modem port
move.w (sp)+,d0 ; get rid of dummy result
bra.s NextEvent
;----------------- Update -----------------
Update
move.l WindowHdl(a5),-(sp)
_BeginUpdate
pea ViewRect ; erase visible window
_EraseRect
pea ViewRect
move.l a2,-(sp)
_TEUpdate
move.l WindowHdl(a5),-(sp)
_EndUpdate
bra.s NextEvent
;---------------- Activate ----------------
Activate
move.l WindowHdl(a5),d0
cmp.l Message(a5),d0 ; our window?
bne.s NextEvent
btst #0,d1 ; activate?
beq.s Deactivate ; no
move.l a2,-(sp) ; move Text Handle To Stack
_TEActivate
move.l EditMHdl(a5),-(sp) ; get handle to the menu
move.w #UndoItem,-(sp) ; disable 1st item (undo)
_DisableItem
SetOurPort
move.l WindowHdl(a5),-(sp)
_SetPort
bra.s NextEvent
; Deactivate window, turn off TextEdit, enable undo for the desk
; accessories.
Deactivate
move.l a2,-(sp) ; get Text Handle
_TeDeActivate
move.l EditMHdl(a5),-(sp) ; get handle to the menu
move.w #UndoItem,-(sp) ; enable 1st item (undo)
_EnableItem
; bra NextEvent ; *CAUTION!* if more code
; is added after this routine, remember to uncomment the line above!
;--------------- Next Event ---------------
NextEvent
moveq #0,d0 ; say that it's not Quit
rts ; return to EventLoop
;------------ Event variables ------------
; IMPORTANT! remember, the EventRecord is
; very much a variable and NOT a constant.
; Never declare the EventRecord variables
; with a dc command.
EventRecord ds.w 0
What: ds.w 1 ; event number
Message: ds.l 1
When: ds.l 1
Point: ds.l 1 ; mouse coordinates
Modify: ds.w 1 ; key and button state
;------------- Window stuff ---------------
WWindow ds.l 1
ViewRect dc.w 5,4,290,500 ; Text Record's View Rect
DestRect dc.w 5,4,290,500 ; Text Record's Dest Rect
WBounds dc.w 5,5,335,510
;------- Application variables ------------
AppleMHdl ds.l 1 ; handle for apple menu
FileMHdl ds.l 1 ; handle for file menu
EditMHdl ds.l 1 ; handle for edit menu
CmdMHdl ds.l 1 ; handle for command menu
DStorage ds.w DWindLen
DeskName ds.w 16 ; DA name
ItemHit ds.w 1
WindowStorage ds.w WindowSize
WindowHdl ds.l 1 ; handle to the window
GSOffset ds.l 1 ; Get Scrap Offset dummy
;Serial port variables
IOBuf ds.b 512 ; general I/O buffer
Outbuf ds.b 512 ; sanitized version of IOBuf
SerCon ds.w 1 ; current serial port config
BaudChk ds.w 1 ; current checked item
CharCnt ds.l 1 ; # of characters read
end